/******************************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Ltd.                       */
/*                                                                            */
/*  NAME      = mysql_core.c                                                  */
/*  NOTE      = MYSQL协议解析                                                 */
/*  DATE      = 2015/04/20 by guojq                                           */
/******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>

#if defined(FRLINUX4) || defined(FRLINUX5)
    #include "endian_dbg.h"
#else
    #include <endian.h>
#endif

#include "frservice_comm.h"
#include "mysql_core.h"
#include "mysql_request.h"
#include "mysql_response.h"
#include "config.h"

#define MYSQL_HEADER_LEN          4      /* mysql数据报文的消息头长度 */
#define MYSQL_MSG_LEN             3      /* mysql数据报文的消息头的消息长度字段 */

/* client/server capabilities */
#define MYSQL_CAPS_LP 0x0001
#define MYSQL_CAPS_FR 0x0002
#define MYSQL_CAPS_LF 0x0004
#define MYSQL_CAPS_CD 0x0008
#define MYSQL_CAPS_NS 0x0010
#define MYSQL_CAPS_CP 0x0020
#define MYSQL_CAPS_OB 0x0040
#define MYSQL_CAPS_LI 0x0080
#define MYSQL_CAPS_IS 0x0100
#define MYSQL_CAPS_CU 0x0200
#define MYSQL_CAPS_IA 0x0400
#define MYSQL_CAPS_SL 0x0800
#define MYSQL_CAPS_II 0x1000
#define MYSQL_CAPS_TA 0x2000
#define MYSQL_CAPS_RS 0x4000
#define MYSQL_CAPS_SC 0x8000

typedef struct __value_string {
    int i_key;
    const char *p_value;
} value_string;

FR_STATIC const value_string mysql_collation_vals[] = {
	{3,   "dec8 COLLATE dec8_swedish_ci"},
	{4,   "cp850 COLLATE cp850_general_ci"},
	{5,   "latin1 COLLATE latin1_german1_ci"},
	{6,   "hp8 COLLATE hp8_english_ci"},
	{7,   "koi8r COLLATE koi8r_general_ci"},
	{8,   "latin1 COLLATE latin1_swedish_ci"},
	{9,   "latin2 COLLATE latin2_general_ci"},
	{10,  "swe7 COLLATE swe7_swedish_ci"},
	{11,  "ascii COLLATE ascii_general_ci"},
	{14,  "cp1251 COLLATE cp1251_bulgarian_ci"},
	{15,  "latin1 COLLATE latin1_danish_ci"},
	{16,  "hebrew COLLATE hebrew_general_ci"},
	{20,  "latin7 COLLATE latin7_estonian_cs"},
	{21,  "latin2 COLLATE latin2_hungarian_ci"},
	{22,  "koi8u COLLATE koi8u_general_ci"},
	{23,  "cp1251 COLLATE cp1251_ukrainian_ci"},
	{25,  "greek COLLATE greek_general_ci"},
	{26,  "cp1250 COLLATE cp1250_general_ci"},
	{27,  "latin2 COLLATE latin2_croatian_ci"},
	{29,  "cp1257 COLLATE cp1257_lithuanian_ci"},
	{30,  "latin5 COLLATE latin5_turkish_ci"},
	{31,  "latin1 COLLATE latin1_german2_ci"},
	{32,  "armscii8 COLLATE armscii8_general_ci"},
	{33,  "utf8 COLLATE utf8_general_ci"},
	{36,  "cp866 COLLATE cp866_general_ci"},
	{37,  "keybcs2 COLLATE keybcs2_general_ci"},
	{38,  "macce COLLATE macce_general_ci"},
	{39,  "macroman COLLATE macroman_general_ci"},
	{40,  "cp852 COLLATE cp852_general_ci"},
	{41,  "latin7 COLLATE latin7_general_ci"},
	{42,  "latin7 COLLATE latin7_general_cs"},
	{43,  "macce COLLATE macce_bin"},
	{44,  "cp1250 COLLATE cp1250_croatian_ci"},
	{47,  "latin1 COLLATE latin1_bin"},
	{48,  "latin1 COLLATE latin1_general_ci"},
	{49,  "latin1 COLLATE latin1_general_cs"},
	{50,  "cp1251 COLLATE cp1251_bin"},
	{51,  "cp1251 COLLATE cp1251_general_ci"},
	{52,  "cp1251 COLLATE cp1251_general_cs"},
	{53,  "macroman COLLATE macroman_bin"},
	{57,  "cp1256 COLLATE cp1256_general_ci"},
	{58,  "cp1257 COLLATE cp1257_bin"},
	{59,  "cp1257 COLLATE cp1257_general_ci"},
	{63,  "binary COLLATE binary"},
	{64,  "armscii8 COLLATE armscii8_bin"},
	{65,  "ascii COLLATE ascii_bin"},
	{66,  "cp1250 COLLATE cp1250_bin"},
	{67,  "cp1256 COLLATE cp1256_bin"},
	{68,  "cp866 COLLATE cp866_bin"},
	{69,  "dec8 COLLATE dec8_bin"},
	{70,  "greek COLLATE greek_bin"},
	{71,  "hebrew COLLATE hebrew_bin"},
	{72,  "hp8 COLLATE hp8_bin"},
	{73,  "keybcs2 COLLATE keybcs2_bin"},
	{74,  "koi8r COLLATE koi8r_bin"},
	{75,  "koi8u COLLATE koi8u_bin"},
	{77,  "latin2 COLLATE latin2_bin"},
	{78,  "latin5 COLLATE latin5_bin"},
	{79,  "latin7 COLLATE latin7_bin"},
	{80,  "cp850 COLLATE cp850_bin"},
	{81,  "cp852 COLLATE cp852_bin"},
	{82,  "swe7 COLLATE swe7_bin"},
	{83,  "utf8 COLLATE utf8_bin"},
	{92,  "geostd8 COLLATE geostd8_general_ci"},
	{93,  "geostd8 COLLATE geostd8_bin"},
	{94,  "latin1 COLLATE latin1_spanish_ci"},
	{99,  "cp1250 COLLATE cp1250_polish_ci"},
	{192, "utf8 COLLATE utf8_unicode_ci"},
	{193, "utf8 COLLATE utf8_icelandic_ci"},
	{194, "utf8 COLLATE utf8_latvian_ci"},
	{195, "utf8 COLLATE utf8_romanian_ci"},
	{196, "utf8 COLLATE utf8_slovenian_ci"},
	{197, "utf8 COLLATE utf8_polish_ci"},
	{198, "utf8 COLLATE utf8_estonian_ci"},
	{199, "utf8 COLLATE utf8_spanish_ci"},
	{200, "utf8 COLLATE utf8_swedish_ci"},
	{201, "utf8 COLLATE utf8_turkish_ci"},
	{202, "utf8 COLLATE utf8_czech_ci"},
	{203, "utf8 COLLATE utf8_danish_ci"},
	{204, "utf8 COLLATE utf8_lithuanian_ci"},
	{205, "utf8 COLLATE utf8_slovak_ci"},
	{206, "utf8 COLLATE utf8_spanish2_ci"},
	{207, "utf8 COLLATE utf8_roman_ci"},
	{208, "utf8 COLLATE utf8_persian_ci"},
	{209, "utf8 COLLATE utf8_esperanto_ci"},
	{210, "utf8 COLLATE utf8_hungarian_ci"},
	{0, NULL}
};

extern void *g_p_apm_context;

/*******************************************************************************
*  FUNC     :  释放分片包信息
*  ARGS     :  p_st_fragment (分片包信息)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
void __mysql_init_fragment(L7_fragment_mysql *p_st_fragment)
{
    if (p_st_fragment == NULL)
        return;

    if (p_st_fragment->p_st_data)
        bytebuff_free(&p_st_fragment->p_st_data);
    
    p_st_fragment->i_remain_head_len = 0;
    p_st_fragment->u64_total_len = 0;
    p_st_fragment->u64_remain_len = 0;
//    p_st_fragment->u64_parsed_index = 0;
    memset(p_st_fragment->arr_remain_packet_head, 0x0, sizeof(p_st_fragment->arr_remain_packet_head));

    return;
}


/*******************************************************************************
*  FUNC     :  释放分片包信息
*  ARGS     :  p_st_fragment (分片包信息)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
void mysql_destroy_fragment(L7_fragment_mysql **pp_st_fragment)
{
    if (pp_st_fragment == NULL || *pp_st_fragment == NULL)
        return;
    L7_fragment_mysql *p_st_fragment = *pp_st_fragment;

    if (p_st_fragment->p_st_data)
        bytebuff_free(&p_st_fragment->p_st_data);

    frfree(p_st_fragment);
    *pp_st_fragment = NULL;

    return;
}

/*******************************************************************************
*  FUNC     :  释放 session的部分资源
*  ARGS     :  p_st_private(私有session数据)
*  RTN      :  FR_SUCC       (normal)
*              FR_FAIL       (error)
*  NOTE     :  p_st_private必须不是NULL
*******************************************************************************/
FR_STATIC void __mysql_destroy_curr_info(L7session_mysql_private *p_st_private)
{
    if (p_st_private->p_st_req)
        mysql_destroy_request(&p_st_private->p_st_req);
    if (p_st_private->p_st_resp)
        mysql_destroy_response(&p_st_private->p_st_resp);
    
    __mysql_init_fragment(p_st_private->p_st_fragment);

    return;
}

/*******************************************************************************
*  FUNC     :  私有信息销毁
*  ARGS     :  p_session(MYSQL的私有session数据)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
void mysql_destroy_session(L7session_mysql **pp_session)
{
    L7session_mysql *p_session = NULL;
    
    if (pp_session == NULL || *pp_session == NULL)
        return;
    
    p_session = *pp_session;
    
    if (p_session->p_charset)
        frfree(p_session->p_charset);
    if (p_session->p_db_user)
        frfree(p_session->p_db_user);
    if (p_session->p_db_name)
        frfree(p_session->p_db_name);
    frfree(p_session);
    *pp_session = NULL;

    return;
}

/*******************************************************************************
*  FUNC     :  私有信息销毁
*  ARGS     :  p_private_session(Sql Server的私有session数据)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
int mysql_session_destroy(void *p_private_session)
{
    L7session_mysql_private *p_st_private;

    /* 判断session是否为空 */
    if (p_private_session == NULL)
        return FR_SUCC;

    p_st_private = (L7session_mysql_private *)p_private_session;
    /* 设置该链路不在session中 */
    p_st_private->i_in_session = MYSQL_FALSE;

    /* 销毁session数据 */
    if (p_st_private->p_st_sess)
        mysql_destroy_session(&(p_st_private->p_st_sess));

    /* 销毁request数据 */
    if (p_st_private->p_st_req)
        mysql_destroy_request(&(p_st_private->p_st_req));

    /* 销毁response数据 */
    if (p_st_private->p_st_resp)
        mysql_destroy_response(&p_st_private->p_st_resp);

    /* 销毁分片数据 */
    if (p_st_private->p_st_fragment)
        mysql_destroy_fragment(&p_st_private->p_st_fragment);

    frfree(p_st_private);

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  初始化私有MYSQL session
*  ARGS     :  p_st_session(MYSQL私有session)
*  RTN      :  FR_SUCC       (normal)
*              FR_FAIL       (error)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __mysql_init_private(L7_session *p_st_session)
{
    L7session_mysql  *p_st_mysql_conn = NULL;                  /* 链接信息 */
    L7_fragment_mysql *p_st_fragment = NULL;                   /* 分片包相关数据 */
    L7session_mysql_private *p_st_private_session = NULL;      /* MYSQL私有session */

    /* 申请整个私有session结构的内存 */
    p_st_private_session = (L7session_mysql_private *)frmalloc(sizeof(L7session_mysql_private));
    if (p_st_private_session == NULL)
        goto error;

    memset(p_st_private_session, 0x00, sizeof(L7session_mysql_private));

    /* 申请链路信息结构内存 */
    p_st_mysql_conn = (L7session_mysql *)frmalloc(sizeof(L7session_mysql));
    if (p_st_mysql_conn == NULL)
        goto error;

    memset(p_st_mysql_conn, 0x00, sizeof(L7session_mysql));
    
    /* 申请分片包信息结构内存 */
    p_st_fragment = (L7_fragment_mysql *)frmalloc(sizeof(L7_fragment_mysql));
    if (p_st_fragment == NULL)
        goto error;
    memset(p_st_fragment, 0x00, sizeof(L7_fragment_mysql));

    /* 设置初始化结构 */
    p_st_private_session->p_st_sess = p_st_mysql_conn;
    /* 设置当前链路处于session中 */
    p_st_private_session->i_in_session = MYSQL_FALSE;
    p_st_private_session->p_st_fragment = p_st_fragment;
    
    p_st_session->p_session_private = p_st_private_session;
    
    return FR_SUCC;

error:
    if (p_st_private_session)
        frfree(p_st_private_session);
    if (p_st_mysql_conn)
        frfree(p_st_mysql_conn);
    if (p_st_fragment)
        frfree(p_st_fragment);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  判断当前数据链路是否是mysql协议
*  ARGS     :  p_st_conn(入力链接信息)
*           :  p_st_packet(入力数据包)
*  RTN      :  APM_L7_IS_NO   (NO)
*              APM_L7_IS_YES  (YES)
*  NOTE     :
*******************************************************************************/
int mysql_detect(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    unsigned int i_len = 0;                                      /* mysql数据长度 */
    char arr_tmp_len[4];
    unsigned int *p_i_tmp = NULL;
    unsigned int ui_len = 0; /* packet数据长度 */
    char *p_data = NULL;             /* packet数据内容 */

    if (p_st_conn == NULL || p_st_packet == NULL)
        return APM_L7_IS_NO;
    
    ui_len = p_st_packet->ui_payload_len;
    p_data = p_st_packet->p_payload;
    /*
    * MySql的packet header占据四个字节,
    * 所以MySql的packet的长度范围必须是大于4且小于等于16777215(0xFFFFFF)
    */
    //printf("mysql_detect,packet_len=%d\n", ui_len);
    if (ui_len < 4 || ui_len > 16777215)
        return APM_L7_IS_NO;
    
    /*
    *  MySql的数据体长度占据三个字节
    */
    memset(arr_tmp_len, 0x00, sizeof(arr_tmp_len));
    memcpy(arr_tmp_len, p_data, 3);
    p_i_tmp = (unsigned int *)arr_tmp_len;
    i_len = *p_i_tmp;
    
    if (i_len + MYSQL_HEADER_LEN != ui_len)
    {
        int i_ret = 0;

        if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
        {
            i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
            if (i_ret == FR_SUCC)
            {
                frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                return APM_L7_IS_YES;
            }
            i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
            if (i_ret == FR_SUCC)
            {
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                return APM_L7_IS_YES;
            }
        }
        else
        {
            i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
            if (i_ret == FR_SUCC)
                return APM_L7_IS_YES;
        }
        return APM_L7_IS_NO;
    }
    frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);

    return APM_L7_IS_YES;
}

Frreport_body_data* mysql_new_report_body(Conntrack *p_st_conn
                                        , L7session_mysql *p_st_sess)
{
    int i_ret = FR_SUCC;
    Frreport_body_data *p_st_sess_body = NULL;

    /* 创建report body */
    p_st_sess_body = frreport_body_new_l7(p_st_conn);
    if (p_st_sess_body == NULL)
    {
        return NULL;
    }
    /* 追加connection相关信息 */
    i_ret = frreport_body_l7_add_connInfo(p_st_sess_body, p_st_conn);
    if (i_ret != FR_SUCC)
    {
        frreport_body_free(&p_st_sess_body);
        return NULL;
    }

//    /* add server */
//    if (p_st_sess->p_program)
//    {
//        i_ret = frreport_body_l7_add_str(p_st_sess_body
//                                         , APM_REPORT_KEY_SESSION_SERVER
//                                         , p_st_sess->p_program);
//        if (i_ret != FR_SUCC)
//        {
//            frreport_body_free(&p_st_sess_body);
//            return NULL;
//        }
//    }
    /* add username */
    if (p_st_sess->p_db_user)
    {
        i_ret = frreport_body_l7_add_str(p_st_sess_body
                                         , APM_REPORT_KEY_SESSION_USERNAME
                                         , p_st_sess->p_db_user);
        if (i_ret != FR_SUCC)
        {
            frreport_body_free(&p_st_sess_body);
            return NULL;
        }
    }
    /* add dbname */
    if (p_st_sess->p_db_name)
    {
        i_ret = frreport_body_l7_add_str(p_st_sess_body
                                         , MYSQL_REPORT_KEY_DBNAME
                                         , p_st_sess->p_db_name);
        if (i_ret != FR_SUCC)
        {
            frreport_body_free(&p_st_sess_body);
            return NULL;
        }
    }
    if (p_st_sess->st_login.tv_sec && p_st_sess->st_login.tv_usec)
    {
        /* add login start time */
        i_ret = frreport_body_l7_add_timeval(p_st_sess_body
                                            , APM_REPORT_KEY_SESSION_LOGIN_TIMESTAMP
                                            , &p_st_sess->st_login);
        if (i_ret != FR_SUCC)
        {
            frreport_body_free(&p_st_sess_body);
            return NULL;
        }
    }

    return p_st_sess_body;
}

/*******************************************************************************
*  FUNC     :  解析mysql头数据
*  ARGS     :  p_st_private(私有session数据)
*           :  p_data(头数据)
*  RTN      :  FR_FAIL (true)
*              FR_SUCC (false)
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __dissect_mysql_header(L7session_mysql_private *p_st_private
                                , char *p_data)
{
    char *p_body_len = NULL;
    uint32_t *p_i_tmp = NULL;
    unsigned int i_index = 0;
    
    if (p_st_private == NULL || p_data == NULL)
        return FR_FAIL;
    
    /*
        消息头占用固定的4个字节
        报文结构:
            3bytes消息长度+1byte序列号
    */
    
    //获取数据包的序列号
    i_index = p_data[MYSQL_MSG_LEN];
    //检查序列号正确性，数据包的序列号从0开始逐一增加
    if (p_st_private->p_st_fragment->u64_parsed_index + 1 != i_index && i_index != 0)
        return FR_FAIL;
    
    p_st_private->p_st_fragment->u64_parsed_index = i_index;
    
    p_body_len = (char *)frmalloc(sizeof(uint32_t));
    if (p_body_len == NULL)
        return FR_FAIL;
    
    memset(p_body_len, 0x00, sizeof(uint32_t));
    
    memcpy(p_body_len, p_data, MYSQL_MSG_LEN);
    
    p_i_tmp = (uint32_t *)p_body_len;
    
    //todo
    p_st_private->p_st_fragment->u64_total_len = le32toh(*p_i_tmp);
    
    frfree(p_body_len);
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  解析mysql握手初始化报文
*  ARGS     :  p_st_private(私有session数据)
*  RTN      :  FR_FAIL (true)
*              FR_SUCC (false)
*  NOTE     :  
*******************************************************************************/
int mysql_dissect_greeting(L7session_mysql_private *p_st_private)
{
    L7_fragment_mysql *p_st_fragment = NULL;
    Frbyte_Packet *p_st_data = NULL;
    uint8_t c_protocol;
    int i_ret = 0;
    
    if (p_st_private == NULL)
        return APM_L7_ANALYZE_FAIL;
    
    /*
        协议版本号               1 BYTE
        服务器版本信息           N BYTES
        服务器线程ID             4 BYTES
        挑战随机数               8 BYTES
        填充值0x00               1 BYTE
        服务器权能标志           2 BYTES
        字符编码                 1 BYTE
        
    */
    p_st_fragment = p_st_private->p_st_fragment;
    p_st_data = p_st_fragment->p_st_data;
    
    i_ret = bytebuff_get_char(p_st_data, (uint8_t *)&c_protocol);
    if (i_ret != FR_SUCC)
        return APM_L7_ANALYZE_FAIL;
    
    //错误数据包
    if (c_protocol == 0xff)
        return APM_L7_ANALYZE_FAIL;
    
    return APM_L7_ANALYZE_SUCC;
}

/*******************************************************************************
*  FUNC     :  Frdata_field释放
*  ARGS     :  pp_st_value(Frdata_field)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __mysql_frdata_free(Frdata_field **pp_st_value)
{
    if (pp_st_value == NULL || *pp_st_value == NULL)
    {
        return;
    }

    Frdata_field *p_st_data = *pp_st_value;
    if (p_st_data->p_field_name)
    {
        frfree(p_st_data->p_field_name);
    }
    if (p_st_data->p_field_value)
    {
        frfree(p_st_data->p_field_value);
    }

    frfree(p_st_data);
    *pp_st_value = NULL;
    return;
}

/*******************************************************************************
*  FUNC     :  Frdata_field释放
*  ARGS     :  pp_st_value(Frdata_field)
*  RTN      :  void
*  NOTE     :
*******************************************************************************/
FR_STATIC void __mysql_frdata_node_free(Frlist_node *p_st_node)
{
    if( p_st_node == NULL || p_st_node->data == NULL)
    {
        return;
    }

    Frdata_field *p_st_data = (Frdata_field *)p_st_node->data;
    if (p_st_data->p_field_name)
    {
        frfree(p_st_data->p_field_name);
    }
    if (p_st_data->p_field_value)
    {
        frfree(p_st_data->p_field_value);
    }

    frfree(p_st_node->data);
    p_st_node->data = NULL;
    return;
}

/*******************************************************************************
*  FUNC     :  根据session创建session data列表
*  ARGS     :  p_st_session (sqlserver session数据)
*           :  pp_lst_session_data (出力session data列表)
*  RTN      :  APM_L7_ANALYZE_SUCC  (success)
*              APM_L7_ANALYZE_FAIL  (failed)
*  NOTE     :  p_st_session必须不为NULL
*******************************************************************************/
int mysql_create_session_data(L7session_mysql *p_st_session
                                 , Frlist_header **pp_lst_session_data)
{
    int i_field_index = 0;
    Frdata_field *p_st_frdata = NULL;
    Frlist_node *p_st_node = NULL;
    Frlist_header *p_lst_session_data = NULL;

    /* 创建list header */
    p_lst_session_data = (Frlist_header *)frmalloc(sizeof(Frlist_header));
    if (p_lst_session_data == NULL)
    {
        return APM_L7_ANALYZE_FAIL;
    }
    memset(p_lst_session_data, 0x0, sizeof(Frlist_header));

    /* 客户端主机名 */
    if (p_st_session->p_db_user)
    {
        p_st_frdata = (Frdata_field *)frmalloc(sizeof(Frdata_field));
        if (p_st_frdata == NULL)
        {
            goto error;
        }
        p_st_frdata->em_data_type = FRSTRING;
        p_st_frdata->p_field_name = frstrdup("CLIENT_HOST");
        p_st_frdata->p_field_value = frstrdup(p_st_session->p_db_user);
        /* 创建list node */
        p_st_node = frlist_node_create(p_st_frdata);
        if (p_st_node == NULL)
        {
            goto error;
        }
        p_st_frdata->i_field_index = ++i_field_index;
        frlist_push(p_lst_session_data, p_st_node);
        p_st_frdata = NULL;
    }
    /* 数据库名 */
    if (p_st_session->p_db_name)
    {
        p_st_frdata = (Frdata_field *)frmalloc(sizeof(Frdata_field));
        if (p_st_frdata == NULL)
        {
            goto error;
        }
        p_st_frdata->em_data_type = FRSTRING;
        p_st_frdata->p_field_name = frstrdup("DB_NAME");
        p_st_frdata->p_field_value = frstrdup(p_st_session->p_db_name);
        /* 创建list node */
        p_st_node = frlist_node_create(p_st_frdata);
        if (p_st_node == NULL)
        {
            goto error;
        }
        p_st_frdata->i_field_index = ++i_field_index;
        frlist_push(p_lst_session_data, p_st_node);
        p_st_frdata = NULL;
    }

    *pp_lst_session_data = p_lst_session_data;
    return APM_L7_ANALYZE_SUCC;

error:
    __mysql_frdata_free(&p_st_frdata);
    frlist_destroy(p_lst_session_data, __mysql_frdata_node_free);
    return APM_L7_ANALYZE_FAIL;
}

int mysql_dissect_login(Conntrack *p_st_conn
                      , L7session_mysql_private *p_st_private
                      , Frhash_header *p_st_hash_report)
{
    L7_fragment_mysql *p_st_fragment = NULL;
    Frbyte_Packet *p_st_data = NULL;
    int i_login_flag = 0;
    int i_charset = 0;
    int i_ret = 0;
    int i_packet_left = 0;
    char *p_username = NULL;
    
    if (p_st_conn== NULL || p_st_private == NULL || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;
    
    if (p_st_private->p_st_req == NULL)
    {
        p_st_private->p_st_req = (L7_req_mysql *)frmalloc(sizeof(L7_req_mysql));
        if (p_st_private->p_st_req == NULL)
            return APM_L7_ANALYZE_FAIL;
        memset(p_st_private->p_st_req, 0x0, sizeof(L7_req_mysql));
    }
    if (p_st_private->p_st_sess)
        p_st_private->p_st_sess->i_login = MYSQL_LOGIN_FAIL;
    
    p_st_fragment = p_st_private->p_st_fragment;
    p_st_data = p_st_fragment->p_st_data;
    
    i_ret = bytebuff_get_int(p_st_data, 2, (uint32_t *)&i_login_flag, BYTE_LITTLE_ENDIAN);
    if (i_ret != FR_SUCC)
        return APM_L7_ANALYZE_FAIL;
    
    /* 4.1 protocol */
    if (i_login_flag & MYSQL_CAPS_CU)
    {
        /*
            客户端权能标志扩展    2BYTES
            最大消息长度          4BYTES
            字符编码              1BYTE
            填充值                23BYTES
            用户名
        */
        p_st_private->p_st_sess->i_version = MYSQL_VERSION_NEW;
        i_ret = bytebuff_skip(p_st_data, 6);
        if (i_ret != FR_SUCC)
            return APM_L7_ANALYZE_FAIL;
        
        //取得字符编码
        i_ret = bytebuff_get_int(p_st_data, 1, (uint32_t *)&i_charset, BYTE_LITTLE_ENDIAN);
        if (i_ret != FR_SUCC)
            return APM_L7_ANALYZE_FAIL;
        
        if (i_charset > 0 && i_charset < 30)
            p_st_private->p_st_sess->p_charset = frstrdup(mysql_collation_vals[i_charset].p_value);
        
        //跳过填充值 
        i_ret = bytebuff_skip(p_st_data, 23);
        if (i_ret != FR_SUCC)
            return APM_L7_ANALYZE_FAIL;
    }
    else
    {
        /*  pre 4.1
            最大消息长度      3BYTES
            用户名
        */
        p_st_private->p_st_sess->i_version = MYSQL_VERSION_OLD;
        i_ret = bytebuff_skip(p_st_data, 3);
        if (i_ret != FR_SUCC)
            return APM_L7_ANALYZE_FAIL;
    }
    
    //取得用户名
    i_packet_left = bytebuff_remaining(p_st_data);
    if (i_packet_left <= 0)
        return APM_L7_ANALYZE_FAIL;
    
    p_username = (char *)frmalloc(i_packet_left + 1);
    if (p_username == NULL)
        return APM_L7_ANALYZE_FAIL;
    
    i_ret = bytebuff_get_bytes(p_st_data, p_username, i_packet_left);
    if (i_ret != FR_SUCC)
    {
        frfree(p_username);
        return APM_L7_ANALYZE_FAIL;
    }
    p_username[i_packet_left] = '\0';
    
    p_st_private->p_st_sess->p_db_user = p_username;
    
    p_st_private->p_st_req->u8_request_type = MYSQL_REQUEST_LONGIN;
    
    p_st_private->i_in_session = MYSQL_TRUE;
    
    return APM_L7_ANALYZE_SUCC;
}

/*******************************************************************************
*  FUNC     :  解析mysql体数据
*  ARGS     :  p_st_private  (私有session数据)
*           :  p_st_timestamp(当前数据包发生的时间点)
*  RTN      :  FR_FAIL (true)
*              FR_SUCC (false)
*  NOTE     :  
*******************************************************************************/
FR_STATIC int __dissect_mysql(Conntrack *p_st_conn
                         , L7session_mysql_private *p_st_private
                         , Frhash_header *p_st_hash_report
                         , Apm_packet *p_st_packet
                         , struct timeval *p_st_timestamp)
{
    L7_fragment_mysql *p_st_fragment = NULL;
    Frbyte_Packet *p_st_data = NULL;
    int i_ret = 0;
    
    if (p_st_private == NULL || p_st_timestamp == NULL)
        return FR_FAIL;
    
    p_st_fragment = p_st_private->p_st_fragment;
    p_st_data = p_st_fragment->p_st_data;
    
    if (p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
    {
        //login
        if (p_st_fragment->u64_parsed_index == 1)
        {
            if (!(p_st_private->p_st_sess && p_st_private->p_st_sess->i_login != MYSQL_LOGIN_UNKNOWN))
            {
                i_ret = mysql_dissect_login(p_st_conn, p_st_private, p_st_hash_report);
                return i_ret;
            }
        }
        i_ret = mysql_dissect_request(p_st_conn, p_st_packet, p_st_private, p_st_hash_report, p_st_timestamp);
    }
    else if (p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT)
    {
        if (p_st_fragment->u64_parsed_index == 0  && p_st_private->p_st_sess->i_greet == 0)
        {
            i_ret = mysql_dissect_greeting(p_st_private);
            p_st_private->p_st_sess->i_greet = 1;
        }
        else
        {
            i_ret = mysql_dissect_response(p_st_conn, p_st_private, p_st_hash_report, p_st_timestamp);
        }
    }
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  取得完整的packet包
*  ARGS     :  p_st_private(私有session数据)
*           :  p_data(当前数据包数据)
*           :  ui_len(当前数据包数据长度)
*           :  p_st_timestamp(当前数据包发生的时间点)
*  RTN      :  FR_FAIL (true)
*              FR_SUCC (false)
*  NOTE     :  解析失败后需要释放p_st_private里的request/response/fragment信息
*******************************************************************************/
FR_STATIC int __mysql_set_analysis_data(L7session_mysql_private *p_st_private
                                    , Conntrack *p_st_conn
                                    , Frhash_header *p_st_hash_report
                                    , Apm_packet *p_st_packet
                                    , char *p_data
                                    , unsigned int ui_len
                                    , struct timeval *p_st_timestamp)
{
    unsigned int ui_remain_len = 0;
    int i_ret = 0;
    int i_flag_do_header = 0;
    int i_result = 0;
    int i_skip = 0;
    L7_fragment_mysql *p_st_fragment = p_st_private->p_st_fragment;
    
    //上一次的头数据包未完全解析，继续解析头数据
    if (p_st_fragment->i_remain_head_len)
    {
        unsigned int i_head_left = MYSQL_HEADER_LEN - p_st_fragment->i_remain_head_len;
        
        if (ui_len < i_head_left)
        {
            memcpy(p_st_fragment->arr_remain_packet_head + p_st_fragment->i_remain_head_len, p_data, ui_len);
            p_st_fragment->i_remain_head_len += ui_len;
            return APM_L7_ANALYZE_TODO;
        }
        memcpy(p_st_fragment->arr_remain_packet_head + p_st_fragment->i_remain_head_len, p_data, i_head_left);
        //解析mysql头数据
        i_ret = __dissect_mysql_header(p_st_private, p_st_fragment->arr_remain_packet_head);
        if (i_ret != FR_SUCC)
            goto err;
        p_data += i_head_left;
        ui_len -= i_head_left;
        p_st_fragment->i_remain_head_len = 0;
        memset(p_st_fragment->arr_remain_packet_head, 0x00, sizeof(p_st_fragment->arr_remain_packet_head));
        i_flag_do_header = 1;
        //检查数据体的头是否为需要跳过的数据内容
        if (ui_len >= 1 && p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT)
        {
            i_result = (uint8_t)(p_data[0]);
            i_skip = mysql_check_response(p_st_private, i_result);
            if (i_skip == MYSQL_RESPONSE_ERR)
                goto err;
            else if (i_skip == MYSQL_RESPONSE_SKIP)
            {
                i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, 1);
                if (i_ret != FR_SUCC)
                    goto err;
                
                p_st_fragment->u64_total_len -= 1;
                p_data += 1;
                ui_len -= 1;
                bytebuff_set_skip(p_st_fragment->p_st_data, p_st_fragment->u64_total_len - 1);
            }
        }
    }
    
    /* 上次packet还遗留的数据长度 */
    ui_remain_len = (unsigned int)p_st_fragment->u64_remain_len;
    
    /* 创建bytebuffer */
    if (p_st_fragment->p_st_data == NULL)
    {
        if (ui_remain_len)
            goto err;
        /* 做成bytebuffer */
        p_st_fragment->p_st_data = bytebuff_creat();
        if (p_st_fragment->p_st_data == NULL)
            goto err;
        /* 设置第一个分片包时间 */
        p_st_fragment->st_first_timestamp.tv_sec = p_st_timestamp->tv_sec;
        p_st_fragment->st_first_timestamp.tv_usec = p_st_timestamp->tv_usec;
    }
    /* 接着上一次的数据 */
    if (ui_remain_len)
    {
        //当前数据不够完成上一次解析
        if (ui_remain_len > ui_len)
        {
            i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, ui_len);
            if (i_ret != FR_SUCC)
                goto err;
            p_st_fragment->u64_remain_len -= ui_len;
            return APM_L7_ANALYZE_TODO;
        }
        i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, ui_remain_len);
        if (i_ret != FR_SUCC)
            goto err;
        
        p_data += ui_remain_len;
        ui_len -= ui_remain_len;
        ui_remain_len = 0;
        p_st_fragment->u64_remain_len = 0;
        
        //解析当前这段数据
        i_ret = __dissect_mysql(p_st_conn, p_st_private, p_st_hash_report, p_st_packet, p_st_timestamp);
        if (i_ret == APM_L7_ANALYZE_FAIL)
            goto err;
        else if (i_ret == APM_L7_ANALYZE_TODO)
        {
            bytebuff_free(&p_st_fragment->p_st_data);
            p_st_fragment->u64_total_len = 0;
            p_st_fragment->i_remain_head_len = 0;
            //继续做下面的数据
            p_st_fragment->p_st_data = bytebuff_creat();
            if (p_st_fragment->p_st_data == NULL)
                goto err;
        }
        //做完了
        else
        {
            bytebuff_free(&p_st_fragment->p_st_data);
            p_st_fragment->u64_total_len = 0;
            p_st_fragment->i_remain_head_len = 0;
            if (ui_len == 0)
                return i_ret;
            goto err;
        }
    }
    
    while (ui_len)
    {
        //长度不够数据包头长度
        if (ui_len < MYSQL_HEADER_LEN)
        {
            p_st_fragment->i_remain_head_len = ui_len;
            memcpy(p_st_fragment->arr_remain_packet_head, p_data, ui_len);
            return APM_L7_ANALYZE_TODO;
        }
        if (i_flag_do_header == 0)
        {
            //解析mysql头数据
            i_ret = __dissect_mysql_header(p_st_private, p_data);
            if (i_ret != FR_SUCC)
                goto err;
            
            p_data += MYSQL_HEADER_LEN;
            ui_len -= MYSQL_HEADER_LEN;
            
            //检查数据体的头是否为需要跳过的数据内容
            if (ui_len >= 1 && p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT)
            {
                i_result = (uint8_t)(p_data[0]);
                i_skip = mysql_check_response(p_st_private, i_result);
                if (i_skip == MYSQL_RESPONSE_ERR)
                    goto err;
                else if (i_skip == MYSQL_RESPONSE_SKIP)
                {
                    i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, 1);
                    if (i_ret != FR_SUCC)
                        goto err;
                    
                    p_st_fragment->u64_total_len -= 1;
                    p_data += 1;
                    ui_len -= 1;
                    bytebuff_set_skip(p_st_fragment->p_st_data, p_st_fragment->u64_total_len - 1);
                }
            }
        }
        else
            i_flag_do_header = 0;
        
        //当前数据不够
        if (p_st_fragment->u64_total_len > ui_len)
        {
            i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, ui_len);
            if (i_ret != FR_SUCC)
                goto err;
            
            p_st_fragment->u64_remain_len = p_st_fragment->u64_total_len - ui_len;
            return APM_L7_ANALYZE_TODO;
        }
        i_ret = bytebuff_add(p_st_fragment->p_st_data, p_data, p_st_fragment->u64_total_len);
        if (i_ret != FR_SUCC)
            goto err;
        ui_len -= p_st_fragment->u64_total_len;
        p_data += p_st_fragment->u64_total_len;
        
        //解析当前这段数据
        i_ret = __dissect_mysql(p_st_conn, p_st_private, p_st_hash_report, p_st_packet, p_st_timestamp);
        if (i_ret == APM_L7_ANALYZE_FAIL)
            goto err;
        else if (i_ret == APM_L7_ANALYZE_TODO)
        {
            bytebuff_free(&p_st_fragment->p_st_data);
            p_st_fragment->u64_total_len = 0;
            p_st_fragment->i_remain_head_len = 0;
            p_st_fragment->p_st_data = bytebuff_creat();
            if (p_st_fragment->p_st_data == NULL)
                goto err;
            //继续做下面的数据
            continue;
        }
        //做完了
        else
        {
            bytebuff_free(&p_st_fragment->p_st_data);
            p_st_fragment->u64_total_len = 0;
            p_st_fragment->i_remain_head_len = 0;
            if (ui_len == 0)
                return i_ret;
            goto err;
        }
    }
    
    return APM_L7_ANALYZE_SUCC;
err:
    __mysql_destroy_curr_info(p_st_private);
    return APM_L7_ANALYZE_FAIL;
}

/*******************************************************************************
*  FUNC     :  MySql的packet解析
*  ARGS     :  p_st_conn(入力链接信息)
            :  p_st_packet(入力packet信息)
            :  p_st_hash_report(保存Frreport_data)
*  RTN      :  参照module.h
*  NOTE     :
*******************************************************************************/
int mysql_analysis(Conntrack *p_st_conn
                  , Apm_packet *p_st_packet
                  , Frhash_header *p_st_hash_report)
{
    L7session_mysql_private *p_st_private = NULL;  /* MYSQL的私有数据 */
    L7_session *p_st_session = NULL;
    int i_ret = 0;

    if (p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;
        
    /* 到Conntrack中取得session信息 */
    p_st_session = frct_get_session(p_st_conn);
    if (p_st_session == NULL)
        return APM_L7_ANALYZE_FAIL;

    /*
    * 如果入力p_st_packet是FIN包, 说明本次MSSQL的connection结束,
    * 释放本次session信息. 如果入力p_st_packet是SYN包,说明新的MSSQL的
    * connection开始了, 释放上次可能残留的session信息
    */
    if (p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN
        || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN)
    {
        /* 销毁所有相关本次MYSQL的connection的数据 */
        mysql_session_destroy((void *)(p_st_session->p_session_private));
        p_st_session->p_session_private = NULL;
        return APM_L7_ANALYZE_SUCC;
    }

    /* 本次链路的第一个有效packet包,创建链路 */
    if (p_st_session->p_session_private == NULL)
    {
        /* 初始化SQL Server的私有数据 */
        i_ret = __mysql_init_private(p_st_session);
        if (i_ret != FR_SUCC)
        {
            return APM_L7_ANALYZE_FAIL;
        }
        /* 设置session ID */
        fruuid(p_st_session->arr_session_id);
    }
    p_st_private = (L7session_mysql_private*)p_st_session->p_session_private;
    /* 设置解析数据 */
    i_ret = __mysql_set_analysis_data(p_st_private
                                    , p_st_conn
                                    , p_st_hash_report
                                    , p_st_packet
                                    , p_st_packet->p_payload
                                    , p_st_packet->ui_payload_len
                                    , &(p_st_packet->st_timestamp));
    
    if (i_ret != APM_L7_ANALYZE_TODO)
        __mysql_init_fragment(p_st_private->p_st_fragment);
    
    return i_ret;
}
